package com.example.goband.api;

import com.example.goband.Service.UserService;
import com.example.goband.common.AppVariable;
import com.example.goband.game.*;
import com.example.goband.model.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.xml.internal.ws.developer.StreamingAttachment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 22479
 * Date: 2023-06-03
 * Time: 11:35
 */
@Component
public class GameAPI extends TextWebSocketHandler {

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private RoomManager roomManager;

    @Resource
    private OnlineUserManager onlineUserManager;

    @Autowired
    private UserService userService;



    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse response = new GameReadyResponse();

        //1. 获取到用户的身份信息 从HttpSession(WebSocketSession)中获取
        User user = (User) session.getAttributes().get(AppVariable.USER_SESSION_KEY);
        GameReadyResponse resp = new GameReadyResponse();

        //2.判定当前用户是否已经在房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if(room == null) {
            //当前用户不在房间
            resp.setOk(false);
            resp.setReason("当前玩家不在房间内");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }

        //3.判断玩家有没有多设备登录 1.判断有没有多设备登录在大厅 2.判断有没有多设备登录在游戏房间
        if(onlineUserManager.getFromGameHall(user.getUserId()) != null ||
            onlineUserManager.getFromGameRoom(user.getUserId()) != null) {
            //玩家多设备登录

            resp.setOk(false);
            resp.setReason("当前玩家多设备登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }

        //4. 当前玩家上线
        onlineUserManager.enterGameRoom(user.getUserId(), session);


        // 5. 把两个玩家加入到游戏房间中.
        //    前面的创建房间/匹配过程, 是在 game_hall.html 页面中完成的.
        //    因此前面匹配到对手之后, 需要经过页面跳转, 来到 game_room.html 才算正式进入游戏房间(才算玩家准备就绪)
        //    当前这个逻辑是在 game_room.html 页面加载的时候进行的.
        //    执行到当前逻辑, 说明玩家已经页面跳转成功了!!
        //    页面跳转,也是一个概率事件  (很有可能出现 "失败" 的情况的)
        synchronized (room) {
            if(room.getUser1() == null) {
                // 第一个玩家还尚未加入房间.
                // 就把当前连上 websocket 的玩家作为 user1, 加入到房间中.
                room.setUser1(user);
                //先进入房间的作为先手
                room.setWhiteUser(user.getUserId());
                System.out.println("玩家1：" + user.getUsername() + "  准备就绪");
                return;
            }
            if(room.getUser2() == null) {
                // 如果进入到这个逻辑, 说明玩家1 已经加入房间, 现在要给当前玩家作为玩家2 了
                room.setUser2(user);
                System.out.println("玩家2：" + user.getUsername() + "  准备就绪");

                // 当两个玩家都加入成功之后, 就要让服务器, 给这两个玩家都返回 websocket 的响应数据.
                // 通知这两个玩家说, 游戏双方都已经准备好了.
                //给user1通知
                noticeRoomReady(room,room.getUser1(),room.getUser2());
                //给user2通知
                noticeRoomReady(room,room.getUser2(),room.getUser1());

                //room的user1和 user2 初始化完毕
                //将 room的相关信息 保存到数据库中去

                return;
            }
        }


        // 6. 此处如果又有玩家尝试连接同一个房间, 就提示报错.
        //    在游戏房间中刷新页面 会跳转到此处 原因:刷新时候room的user1和user2都不为null,仅仅只是游戏大厅状态为下线状态 -- exitGameRoom
        resp.setOk(false);
        resp.setReason("当前房间已满, 您不能加入房间");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

    }

    //通知玩家 服务器向客户端推送消息
    public void noticeRoomReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse resp = new GameReadyResponse();

        resp.setMessage("gameReady");
        resp.setOk(true);
        resp.setRoomId(room.getRoomId());
        resp.setThisUserId(thisUser.getUserId());
        resp.setThatUserId(thatUser.getUserId());
        resp.setWhiteUser(room.getWhiteUser());

        WebSocketSession session = onlineUserManager.getFromGameRoom(thisUser.getUserId());
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        User user = (User) session.getAttributes().get(AppVariable.USER_SESSION_KEY);

        //处理落子请求
        //1.获取room对象
        Room room= roomManager.getRoomByUserId(user.getUserId());
        if(room == null) {
            System.out.println("[handleTextMessage]当前房间已关闭");
            return;
        }

        synchronized (room) {
            //2.通过room对象处理落子请求
            GameResponse resp = room.putChess(message.getPayload());

            //3.给房间中的所有玩家 + 观战玩家 客户端返回响应  --> 获取到所有玩家的WebSocketSession
            WebSocketSession session1 = onlineUserManager.getFromGameRoom(room.getUser1().getUserId());
            WebSocketSession session2 = onlineUserManager.getFromGameRoom(room.getUser2().getUserId());

            //4.判定玩家对话是否为空 | 玩家是否下线
            if(session1 == null) {
                //玩家1 下线
                //玩家2直接获胜
                resp.setWinner(room.getUser2().getUserId());
                System.out.println("玩家1 掉线 | 玩家2 获胜");
            }
            if(session2 == null) {
                //玩家2 下线
                //玩家1直接获胜
                resp.setWinner(room.getUser1().getUserId());
                System.out.println("玩家2 掉线  | 玩家1 获胜");
            }
            String jsonString = objectMapper.writeValueAsString(resp);

            //通知所有玩家
            if(session1 != null) {
                session1.sendMessage(new TextMessage(jsonString));
            }
            if(session2 != null) {
                session2.sendMessage(new TextMessage(jsonString));
            }

            //5.如果此时已经分出了胜负 房间就可以关闭了
            if(resp.getWinner() != 0) {
                //分出胜负 房间销毁
                System.out.println("---游戏结束 | 获胜方:" + resp.getWinner() + "---");
                //操作数据库更新玩家数据

                   int winId = resp.getWinner();
                   int loseId = resp.getWinner() == room.getUser1().getUserId() ? room.getUser2().getUserId() : room.getUser1().getUserId();
                   userService.userWin(winId);
                   userService.userLose(loseId);


                //销毁房间
                roomManager.remove(room.getRoomId(),room.getUser1().getUserId(),room.getUser2().getUserId());
            }
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        //设置玩家下线
        User user = (User) session.getAttributes().get(AppVariable.USER_SESSION_KEY);

        //2.判断玩家有没有多设备登录 1.判断有没有多设备登录在大厅 2.判断有没有多设备登录在游戏房间
        if(onlineUserManager.getFromGameRoom(user.getUserId()) == session) {
            //3.下线操作  |  加上这个判定, 目的是为了避免在多开的情况下, 第二个用户退出连接动作, 导致第一个用户的会话被删除
            onlineUserManager.exitGameRoom(user.getUserId());

            System.out.println("玩家：" + user.getUsername() + "  下线2");

            //当前玩家掉线,通知对手重新匹配
            noticeThatUserWin(user);

        }

        //玩家多设备登录,不做操作


    }



    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        //设置玩家下线
        User user = (User) session.getAttributes().get(AppVariable.USER_SESSION_KEY);
        //2.判断玩家有没有多设备登录 1.判断有没有多设备登录在大厅 2.判断有没有多设备登录在游戏房间
        if(onlineUserManager.getFromGameRoom(user.getUserId()) == session) {
            //3.下线操作 |   加上这个判定, 目的是为了避免在多开的情况下, 第二个用户退出连接动作, 导致第一个用户的会话被删除
            onlineUserManager.exitGameRoom(user.getUserId());

            System.out.println("玩家：" + user.getUsername() + "  下线1");

            //当前玩家掉线,通知对手重新匹配
            noticeThatUserWin(user);
        }

        ///玩家多设备登录,不做操作


    }

    private void noticeThatUserWin(User user) throws IOException {
        //1.根据当前玩家找到游戏房间
        Room room = roomManager.getRoomByUserId(user.getUserId());

        if(room == null) {
            //意味着房间已经被关闭 游戏已经结束,是正常结束,不需要通知对方
            System.out.println("[当前房间游戏正常结束]");
            return;
        }

        //2.根据房间找到对手
        User thatUser = (user == room.getUser1()) ? room.getUser2() : room.getUser1();

        //3.判定对手在线状态
        WebSocketSession session = onlineUserManager.getFromGameRoom(thatUser.getUserId());

        if(session == null) {
            //对手也掉线了,不需要通知
            System.out.println("[当前房间全部玩家掉线]---请检查服务器状态");
            return;
        }

        //4.构造一个响应,通知对手获胜
        GameResponse resp = new GameResponse();
        //4代表某一玩家下线
        resp.setWinner(4);
        resp.setMessage("deuce");
        resp.setUserId(thatUser.getUserId());
        resp.setCol(7);
        resp.setRow(7);
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

        //5.释放房间,防止内存泄漏
        roomManager.remove(room.getRoomId(),room.getUser1().getUserId(),room.getUser2().getUserId());




    }
}
